require 'fileutils'
require 'container/utils'

class MongrelInstance

  include FileUtils
  include Utils

  class << self

    def find_all(controller, options = {})

      host = options.delete(:host)
      port = options.delete(:port)

      raise "Unknown option(s): #{options.keys.join(', ')}" unless options.empty?

      config_pattern = File.join(controller.config_dir, 'mongrel.*.config')
      answer = []
      Dir[config_pattern].sort.each do |config_file|

        match = File.basename(config_file).match(/^mongrel\.(.+)\.(\d+)\.config$/)
        next unless match

        config_host, config_port = match[1..2]

        next if host and config_host != host
        next if port and config_port != port.to_s

        reserve = (not File.exists?(File.join(controller.config_dir, "monit.#{config_host}.#{config_port}.config")))

        answer << MongrelInstance.new(controller, config_host, config_port.to_i, reserve)
      end
      raise "No matching config files found" if answer.empty?
      answer
    end

    def find_active(controller, options = {})
      answer = find_all(controller, options).select { |mongrel| not mongrel.reserve? }
      raise "No matching config files found" if answer.empty?
      answer
    end

    def find_one(controller, options)
      raise "MongrelInstance.find_one: both host and port must be specified" unless options[:host] and options[:port]
      find_all(controller, options).first
    end

    def find_by_config_file(config_file_path)
      details = parse_config_path(config_file_path)
      controller = MongrelController.new(details[:container_root])
      MongrelInstance.new(controller, details[:host], details[:port], details[:reserve])
    end

    def parse_config_path(config_file_path)
      config_directory = File.dirname(config_file_path)
      raise "Container configuration file is supposed to be within 'config' directory" unless File.basename(config_directory) == 'config'
      container_root = File.dirname(config_directory)

      match = File.basename(config_file_path).match(/^mongrel\.(.+)\.(\d+)\.config(\.reserve)?$/)
      raise "File #{config_file_path} does not match naming convention of 'mongrel.<host>.<port>.config[.reserve]'" unless match

      return {
        :container_root => container_root,
        :host => match[1],
        :port => match[2],
        :reserve => !match[3].nil?
      }
    end

  end

  attr_reader :host, :port

  def initialize(controller, host, port, reserve = false)
    @controller, @host, @port, @reserve = controller, host, port, reserve
  end

  def config
    @config ||= YAML.load_file(config_path)
  end

  def start
    puts "Starting Mongrel at #{host}:#{port}"
    puts "  ... logging to #{config[:log_file]}"
    return false unless @controller.rails_app_installed?

    rm_f pid_file
    # cd to application_root is needed until this patch makes it into the next release:
    # http://rubyforge.org/tracker/?func=detail&aid=9326&group_id=1306&atid=5145
    success = execute "cd #{@controller.application_root}; mongrel_rails start --config #{config_path}"

    puts (success ? '  done.' : '  !!! FAILED')
  end

  def stop
    puts "Stopping Mongrel at #{host}:#{port}"
    old_pid = self.pid
    if old_pid
      # -c / works around a stupid thing where Mongrel tries to cd into a current working directory when it wasn't asked to
      execute("mongrel_rails stop -c / --pid #{pid_file}")
    elsif not reserve?
      STDERR.puts "PID file not found: #{pid_file}"
    end
  end

  def pid
    if File.exists?(pid_file)
      File.read(pid_file).strip
    else
      nil
    end
  end

  def to_s
    "mongrel(#{@host}:#{@port}#{'.reserve' if reserve?})"
  end

  def file_name(prefix, extension)
    "#{prefix}.#{@host}.#{@port}.#{extension}"
  end

  def pid_file
    config[:pid_file]
  end

  def generate_config
    # TODO introduce a logger facility and spread the logging goodness
    print "Generating Mongrel config at #{config_path}..."
    application_root = @controller.application_root

    File.open(config_path, 'w') do |f|
      template = File.read(File.join(ROR_SERVICE_ROOT, 'container', 'mongrel.rails.config.template'))
      f.puts(instance_eval('"'+template+'"'))
    end
    puts "   done."
    generate_monit_config
  end

  def generate_monit_config
    print "Generating Monit config at #{monit_config_path}..."
    File.open(monit_config_path, 'a') do |f|
        template = File.read(File.join(ROR_SERVICE_ROOT, 'container', 'monit.conf.template'))
        f.puts eval('"'+template+'"')
    end
    puts "  done."
  end

  def config_path
    File.join(@controller.config_dir, file_name('mongrel', 'config'))
  end

  def monit_config_path
    File.join(@controller.config_dir, file_name('monit', "config#{'.reserve' if reserve?}"))
  end

  def domain
    File.basename(@controller.root)
  end

  def monit_service_name
    "#{domain}_#{host}_#{port}"
  end

  def reserve?
    @reserve
  end

end
